<?xml version="1.0" encoding="utf-8"?>
<!--
                                                                                     
 h       t     t                ::       /     /                     t             / 
 h       t     t                ::      //    //                     t            // 
 h     ttttt ttttt ppppp sssss         //    //  y   y       sssss ttttt         //  
 hhhh    t     t   p   p s            //    //   y   y       s       t          //   
 h  hh   t     t   ppppp sssss       //    //    yyyyy       sssss   t         //    
 h   h   t     t   p         s  ::   /     /         y  ..       s   t    ..   /     
 h   h   t     t   p     sssss  ::   /     /     yyyyy  ..   sssss   t    ..   /     
                                                                                     
	<https://y.st./>
	Copyright © 2017 Alex Yst <mailto:copyright@y.st>

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program. If not, see <https://www.gnu.org./licenses/>.
-->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<base href="https://y.st./en/coursework/CS1103/DrawTextPanel.java.xhtml" />
		<title>DrawTextPanel.java &lt;https://y.st./en/coursework/CS1103/DrawTextPanel.java.xhtml&gt;</title>
		<link rel="icon" type="image/png" href="/link/CC_BY-SA_4.0/y.st./icon.png" />
		<link rel="stylesheet" type="text/css" href="/link/basic.css" />
		<link rel="stylesheet" type="text/css" href="/link/site-specific.css" />
		<script type="text/javascript" src="/script/javascript.js" />
		<meta name="viewport" content="width=device-width" />
	</head>
	<body>
		<nav>
			<p>
				<a href="/en/">Home</a> |
				<a href="/en/a/about.xhtml">About</a> |
				<a href="/en/a/contact.xhtml">Contact</a> |
				<a href="/a/canary.txt">Canary</a> |
				<a href="/en/URI_research/"><abbr title="Uniform Resource Identifier">URI</abbr> research</a> |
				<a href="/en/opinion/">Opinions</a> |
				<a href="/en/coursework/">Coursework</a> |
				<a href="/en/law/">Law</a> |
				<a href="/en/a/links.xhtml">Links</a> |
				<a href="/en/coursework/CS1103/DrawTextPanel.java.xhtml.asc">{this page}.asc</a>
			</p>
			<hr/>
			<p>
				<a href="/en/coursework/BUS1101/" title="Principles of Business Management">BUS 1101</a> |
				<span class="hyperlink_unavailable" title="Basic Accounting">BUS 1102</span> |
				<span class="hyperlink_unavailable" title="Principles of Marketing">BUS 2201</span> |
				<span class="hyperlink_unavailable" title="Multinational Management">BUS 2207</span> |
				<span class="hyperlink_unavailable" title="Business and Society">BUS 3306</span> |
				<a href="/en/coursework/CS1101/" title="Programming Fundamentals">CS 1101</a> |
				<a href="/en/coursework/CS1102/" title="Programming 1">CS 1102</a> |
				<a href="/en/coursework/CS1103/" title="Programming 2">CS 1103</a> |
				<span class="hyperlink_unavailable" title="Computer Systems">CS 1104</span> |
				<a href="/en/coursework/CS2203/" title="Databases 1">CS 2203</a> |
				<span class="hyperlink_unavailable" title="Communications and Networking">CS 2204</span> |
				<a href="/en/coursework/CS2205/" title="Web Programming 1">CS 2205</a> |
				<a href="/en/coursework/CS2301/" title="Operating Systems 1">CS 2301</a> |
				<span class="hyperlink_unavailable" title="Software Engineering 1">CS 2401</span> |
				<span class="hyperlink_unavailable" title="Data Structures">CS 3303</span> |
				<span class="hyperlink_unavailable" title="Analysis of Algorithms">CS 3304</span> |
				<span class="hyperlink_unavailable" title="Web Programming 2">CS 3305</span> |
				<span class="hyperlink_unavailable" title="Databases 2">CS 3306</span> |
				<span class="hyperlink_unavailable" title="Operating Systems 2">CS 3307</span> |
				<span class="hyperlink_unavailable" title="Information Retrieval">CS 3308</span> |
				<span class="hyperlink_unavailable" title="Comparative Programming Languages">CS 4402</span> |
				<span class="hyperlink_unavailable" title="Software Engineering 2">CS 4403</span> |
				<span class="hyperlink_unavailable" title="Advanced Networking and Data Security">CS 4404</span> |
				<span class="hyperlink_unavailable" title="Mobile Applications">CS 4405</span> |
				<span class="hyperlink_unavailable" title="Computer Graphics">CS 4406</span> |
				<span class="hyperlink_unavailable" title="Data Mining and Machine Learning">CS 4407</span> |
				<span class="hyperlink_unavailable" title="Artificial Intelligence">CS 4408</span> |
				<span class="hyperlink_unavailable" title="English Composition 2">ENGL 1102</span> |
				<span class="hyperlink_unavailable" title="World Literature">ENGL 1405</span> |
				<span class="hyperlink_unavailable" title="Introduction to Environmental Science">ENVS 1301</span> |
				<a href="/en/coursework/HIST1421/" title="Greek and Roman Civilization">HIST 1421</a> |
				<span class="hyperlink_unavailable" title="College Algebra">MATH 1201</span> |
				<span class="hyperlink_unavailable" title="Calculus">MATH 1211</span> |
				<span class="hyperlink_unavailable" title="Introduction to Statistics">MATH 1280</span> |
				<span class="hyperlink_unavailable" title="Discrete Mathematics">MATH 1302</span> |
				<span class="hyperlink_unavailable" title="Introduction to Philosophy">PHIL 1402</span> |
				<a href="/en/coursework/PHIL1404/" title="Ethics and Social Responsibility">PHIL 1404</a> |
				<a href="/en/coursework/POLS1503/" title="Globalization">POLS 1503</a> |
				<span class="hyperlink_unavailable" title="Introduction to Psychology">PSYC 1504</span> |
				<a href="/en/coursework/UNIV1001/" title="Online Education Strategies">UNIV 1001</a>
			</p>
			<hr/>
			<p>
				<a href="/en/coursework/CS1103/Unit1.java.xhtml" title="Unit1.java">Unit 1</a> |
				<a href="/en/coursework/CS1103/SimpleRandomSentences.java.xhtml" title="SimpleRandomSentences.java">Unit 2</a> |
				<a href="/en/coursework/CS1103/Tape.java.xhtml" title="Tape.java">Unit 3</a> |
				<a href="/en/coursework/CS1103/Debugging.xhtml" title="Debugging">Unit 4</a> |
				<a href="/en/coursework/CS1103/Unit5.java.xhtml" title="Unit5.java">Unit 5</a> |
				<a href="/en/coursework/CS1103/DrawTextPanel.java.xhtml" title="DrawTextPanel.java">Unit 6</a> |
				<a href="/en/coursework/CS1103/Unit7.java.xhtml" title="Unit7.java">Unit 7</a>
			</p>
			<hr/>
		</nav>
		<header>
			<h1>DrawTextPanel.java</h1>
			<p>Written in <span title="Programming 2">CS 1103</span> of <a href="http://www.uopeople.edu/">University of the People</a>, finalised on 2017-07-26</p>
		</header>
<h2>Source code</h2>
<blockquote>
<pre><code>/* 
 * Copyright (C) 2017 Alex Yst
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see &lt;http://www.gnu.org/licenses/&gt;.
 */
package textcollage;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JColorChooser;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;

/**
 * A panel that contains a large drawing area where strings
 * can be drawn.  The strings are represented by objects of
 * type DrawTextItem.  An input box under the panel allows
 * the user to specify what string will be drawn when the
 * user clicks on the drawing area.
 */
public class DrawTextPanel extends JPanel  {
/**
 * The JPanel class seems to want this version integer implemented in child
 * classes. As far as I know, it&apos;s not used by this program, but adding it gets
 * Eclipse to stop complaining about the fact that it&apos;s missing.
 */
	private static final long serialVersionUID = 0;
/**
 * An array of objects representing strings to draw
 */
	private ArrayList&lt;DrawTextItem&gt; theString = new ArrayList&lt;DrawTextItem&gt;();
	private java.awt.Image image;
	private Color currentTextColor = Color.BLACK;  // Colour applied to new strings.
	private Canvas canvas;  // the drawing area.
	private JTextField input;  // where the user inputs the string that will be added to the canvas
	private SimpleFileChooser fileChooser;  // for letting the user select files
	private JMenuBar menuBar; // a menu bar with command that affect this panel
	private MenuHandler menuHandler; // a listener that responds whenever the user selects a menu command
	private JMenuItem undoMenuItem;  // the &quot;Remove Item&quot; command from the edit menu
/**
 * An object of type Canvas is used for the drawing area.
 * The canvas simply displays all the DrawTextItems that
 * are stored in the ArrayList, strings.
 */
	private class Canvas extends JPanel {
/**
 * The JPanel class seems to want this version integer implemented in child
 * classes. As far as I know, it&apos;s not used by this program, but adding it gets
 * Eclipse to stop complaining about the fact that it&apos;s missing.
 */
	private static final long serialVersionUID = 0;
		Canvas() {
			setPreferredSize( new Dimension(800,600) );
			setBackground(Color.LIGHT_GRAY);
			setFont( new Font( &quot;Serif&quot;, Font.BOLD, 24 ));
		}
		protected void paintComponent(Graphics g) {
			super.paintComponent(g);
			((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
					RenderingHints.VALUE_ANTIALIAS_ON);
			if(image != null) {
				g.drawImage(image, 0, 0, 800, 600, this);
			}
			for(DrawTextItem item : theString) {
				item.draw(g);
			}
		}
	}
	
/**
 * An object of type MenuHandler is registered as the ActionListener
 * for all the commands in the menu bar.  The MenuHandler object
 * simply calls doMenuCommand() when the user selects a command
 * from the menu.
 */
	private class MenuHandler implements ActionListener {
		public void actionPerformed(ActionEvent evt) {
			doMenuCommand( evt.getActionCommand());
		}
	}
/**
 * Creates a DrawTextPanel.  The panel has a large drawing area and
 * a text input box where the user can specify a string.  When the
 * user clicks the drawing area, the string is added to the drawing
 * area at the point where the user clicked.
 */
	public DrawTextPanel() {
		fileChooser = new SimpleFileChooser();
		undoMenuItem = new JMenuItem(&quot;Remove Item&quot;);
		undoMenuItem.setEnabled(false);
		menuHandler = new MenuHandler();
		setLayout(new BorderLayout(3,3));
		setBackground(Color.BLACK);
		setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
		canvas = new Canvas();
		add(canvas, BorderLayout.CENTER);
		JPanel bottom = new JPanel();
		bottom.add(new JLabel(&quot;Text to add: &quot;));
		input = new JTextField(&quot;Hello World!&quot;, 40);
		bottom.add(input);
		add(bottom, BorderLayout.SOUTH);
		canvas.addMouseListener( new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				doMousePress( e );
			}
		} );
	}
/**
 * This method is called when the user clicks the drawing area.
 * A new string is added to the drawing area.  The center of
 * the string is at the point where the user clicked.
 * @param e the mouse event that was generated when the user clicked
 */
	public void doMousePress( MouseEvent e ) {
		String text = input.getText().trim();
		if (text.length() == 0) {
			input.setText(&quot;Hello World!&quot;);
			text = &quot;Hello World!&quot;;
		}
		DrawTextItem s = new DrawTextItem( text, e.getX(), e.getY() );
		s.setTextColor(currentTextColor);  // Default is null, meaning default color of the canvas (black).
		
//   SOME OTHER OPTIONS THAT CAN BE APPLIED TO TEXT ITEMS:
//		s.setFont( new Font( &quot;Serif&quot;, Font.ITALIC + Font.BOLD, 12 ));  // Default is null, meaning font of canvas.
//		s.setMagnification(3);  // Default is 1, meaning no magnification.
//		s.setBorder(true);  // Default is false, meaning don&apos;t draw a border.
//		s.setRotationAngle(25);  // Default is 0, meaning no rotation.
//		s.setTextTransparency(0.3); // Default is 0, meaning text is not at all transparent.
//		s.setBackground(Color.BLUE);  // Default is null, meaning don&apos;t draw a background area.
//		s.setBackgroundTransparency(0.7);  // Default is 0, meaning background is not transparent.
		theString.add(s);
		undoMenuItem.setEnabled(true);
		canvas.repaint();
	}
/**
 * Returns a menu bar containing commands that affect this panel.  The menu
 * bar is meant to appear in the same window that contains this panel.
 */
	public JMenuBar getMenuBar() {
		if (menuBar == null) {
			menuBar = new JMenuBar();

			String commandKey; // for making keyboard accelerators for menu commands
			if (System.getProperty(&quot;mrj.version&quot;) == null)
				commandKey = &quot;control &quot;;  // command key for non-Mac OS
			else
				commandKey = &quot;meta &quot;;  // command key for Mac OS

			JMenu fileMenu = new JMenu(&quot;File&quot;);
			menuBar.add(fileMenu);
			JMenuItem saveItem = new JMenuItem(&quot;Save...&quot;);
			saveItem.setAccelerator(KeyStroke.getKeyStroke(commandKey + &quot;N&quot;));
			saveItem.addActionListener(menuHandler);
			fileMenu.add(saveItem);
			JMenuItem openItem = new JMenuItem(&quot;Open...&quot;);
			openItem.setAccelerator(KeyStroke.getKeyStroke(commandKey + &quot;O&quot;));
			openItem.addActionListener(menuHandler);
			fileMenu.add(openItem);
			fileMenu.addSeparator();
			JMenuItem saveImageItem = new JMenuItem(&quot;Save Image...&quot;);
			saveImageItem.setAccelerator(KeyStroke.getKeyStroke(commandKey + &quot;S&quot;));
			saveImageItem.addActionListener(menuHandler);
			fileMenu.add(saveImageItem);

			JMenu editMenu = new JMenu(&quot;Edit&quot;);
			menuBar.add(editMenu);
			undoMenuItem.addActionListener(menuHandler); // undoItem was created in the constructor
			undoMenuItem.setAccelerator(KeyStroke.getKeyStroke(commandKey + &quot;Z&quot;));
			editMenu.add(undoMenuItem);
			editMenu.addSeparator();
			JMenuItem clearItem = new JMenuItem(&quot;Clear&quot;);
			clearItem.setAccelerator(KeyStroke.getKeyStroke(commandKey + &quot;E&quot;));
			clearItem.addActionListener(menuHandler);
			editMenu.add(clearItem);

			JMenu optionsMenu = new JMenu(&quot;Options&quot;);
			menuBar.add(optionsMenu);
			JMenuItem colorItem = new JMenuItem(&quot;Set Text Color...&quot;);
			colorItem.setAccelerator(KeyStroke.getKeyStroke(commandKey + &quot;T&quot;));
			colorItem.addActionListener(menuHandler);
			optionsMenu.add(colorItem);
			JMenuItem bgColorItem = new JMenuItem(&quot;Set Background Color...&quot;);
			bgColorItem.setAccelerator(KeyStroke.getKeyStroke(commandKey + &quot;B&quot;));
			bgColorItem.addActionListener(menuHandler);
			optionsMenu.add(bgColorItem);

			JMenuItem bgImageItem = new JMenuItem(&quot;Set Background Image...&quot;);
			bgImageItem.setAccelerator(KeyStroke.getKeyStroke(commandKey + &quot;I&quot;));
			bgImageItem.addActionListener(menuHandler);
			optionsMenu.add(bgImageItem);
		}
		return menuBar;
	}
/**
 * Carry out one of the commands from the menu bar.
 * @param command the text of the menu command.
 */
	private void doMenuCommand(String command) {
		if (command.equals(&quot;Save...&quot;)) { // save all the string info to a file
			File file = fileChooser.getOutputFile(this, &quot;Select Text File Name&quot;, &quot;textimage.txt&quot;);
			if(file == null) {
				JOptionPane.showMessageDialog(this, &quot;ERROR: No file name chosen; file save aborted.&quot;);
			} else {
				try {
					java.io.PrintWriter f = new java.io.PrintWriter(file);
					Color b = canvas.getBackground();
					f.print(b.getRed());
					f.print(&apos;	&apos;);
					f.print(b.getGreen());
					f.print(&apos;	&apos;);
					f.println(b.getBlue());
					for(DrawTextItem item : theString) {
						Color c = item.getTextColor();
						f.print(c.getRed());
						f.print(&apos;	&apos;);
						f.print(c.getGreen());
						f.print(&apos;	&apos;);
						f.print(c.getBlue());
						f.print(&apos;	&apos;);
						f.print(item.getX());
						f.print(&apos;	&apos;);
						f.print(item.getY());
						f.print(&apos;	&apos;);
						f.println(item.getString());
					}
					f.close();
				} catch(java.io.FileNotFoundException e) {
					JOptionPane.showMessageDialog(this, &quot;ERROR: &quot;+e.getMessage());
				}
			}
		}
		else if (command.equals(&quot;Open...&quot;)) { // read a previously saved file, and reconstruct the list of strings
			File file = fileChooser.getInputFile(this, &quot;Select Text File&quot;);
			if(file == null) {
				JOptionPane.showMessageDialog(this, &quot;ERROR: No file chosen; file load aborted.&quot;);
			} else {
				try {
					java.util.Scanner f = new java.util.Scanner(file);
// I tried not creating an unused object, but Eclipse wouldn&apos;t stop complaining
// that the variable might not be initialised. This was an especially stupid
// complaint when I set up a check, `if(canvasColour != null)`. Eclipse
// complained both in the check, which was trying to see if it had been set,
// and in the block, which would only be executed if it *had* been set. The
// only way I could figure out how to get the code right while getting Eclipse
// to stop complaining was to construct this useless dummy object that will
// under no circumstances actually get used.
					Color canvasColour = new Color(0,0,0);
					boolean canvasColourConstructed = false;
					ArrayList&lt;DrawTextItem&gt; tempTheString = new ArrayList&lt;DrawTextItem&gt;();
					String[] values = f.nextLine().split(&quot;	&quot;);
					if(values.length &gt; 2) {
						canvasColour = new Color(
							Integer.parseInt(values[0]),
							Integer.parseInt(values[1]),
							Integer.parseInt(values[2])
						);
						canvasColourConstructed = true;
					}
					while(f.hasNext()) {
						values = f.nextLine().split(&quot;	&quot;);
						if(values.length &gt; 5) { // The bare minimum number of values
							Color c = new Color(
								Integer.parseInt(values[0]),
								Integer.parseInt(values[1]),
								Integer.parseInt(values[2])
							);
							DrawTextItem item = new DrawTextItem(
								values[5],
								Integer.parseInt(values[3]),
								Integer.parseInt(values[4])
							);
							item.setTextColor(c);
							tempTheString.add(item);
						}
					}
					f.close();
					theString = tempTheString;
					if(canvasColourConstructed) {
						canvas.setBackground(canvasColour);
					}
					canvas.repaint();
				} catch(java.io.FileNotFoundException e) {
					JOptionPane.showMessageDialog(this, &quot;ERROR: File not found. (&quot;+e.getMessage()+&quot;)&quot;);
				} catch(Exception e) {
					JOptionPane.showMessageDialog(this, &quot;ERROR: File format not supported. (&quot;+e.getMessage()+&quot;)&quot;);
				}
			}
		}
		else if (command.equals(&quot;Clear&quot;)) {
			theString.clear();
			undoMenuItem.setEnabled(false);
			canvas.repaint();
		}
		else if (command.equals(&quot;Remove Item&quot;)) {
			theString.remove(theString.size()-1);
			if(theString.size() == 0) {
				undoMenuItem.setEnabled(false);
			}
			canvas.repaint();
		}
		else if (command.equals(&quot;Set Text Color...&quot;)) {
			Color c = JColorChooser.showDialog(this, &quot;Select Text Color&quot;, currentTextColor);
			if (c != null)
				currentTextColor = c;
		}
		else if (command.equals(&quot;Set Background Color...&quot;)) {
			Color c = JColorChooser.showDialog(this, &quot;Select Background Color&quot;, canvas.getBackground());
			if (c != null) {
				image = null;
				canvas.setBackground(c);
				canvas.repaint();
			}
		}
		else if (command.equals(&quot;Set Background Image...&quot;)) {
			File file = fileChooser.getInputFile(this, &quot;Select Image File&quot;);
			if(file == null) {
				JOptionPane.showMessageDialog(this, &quot;ERROR: No file chosen; file load aborted.&quot;);
			} else {
				String fileType= new javax.activation.MimetypesFileTypeMap().getContentType(file);
				if(fileType.substring(0, 6).equals(&quot;image/&quot;)) {
					try {
						image = ImageIO.read(file);
						canvas.repaint();
					} catch(java.io.FileNotFoundException e) {
						JOptionPane.showMessageDialog(this, &quot;ERROR: File not found. (&quot;+e.getMessage()+&quot;)&quot;);
					} catch(Exception e) {
						JOptionPane.showMessageDialog(this, &quot;ERROR: File format not supported. (&quot;+e.getMessage()+&quot;)&quot;);
					}
				} else {
					JOptionPane.showMessageDialog(this, &quot;ERROR: The selected file does not appear to be an image file.&quot;);
				}
			}
		}
		else if (command.equals(&quot;Save Image...&quot;)) {  // save a PNG image of the drawing area
			File imageFile = fileChooser.getOutputFile(this, &quot;Select Image File Name&quot;, &quot;textimage.png&quot;);
			if (imageFile == null)
				return;
			try {
// Because the image is not available, I will make a new BufferedImage and
// draw the same data to the BufferedImage as is shown in the panel.
// A BufferedImage is an image that is stored in memory, not on the screen.
// There is a convenient method for writing a BufferedImage to a file.
				BufferedImage image = new BufferedImage(canvas.getWidth(),canvas.getHeight(),
						BufferedImage.TYPE_INT_RGB);
				Graphics g = image.getGraphics();
				g.setFont(canvas.getFont());
				canvas.paintComponent(g);  // draws the canvas onto the BufferedImage, not the screen!
				boolean ok = ImageIO.write(image, &quot;PNG&quot;, imageFile); // write to the file
				if (ok == false)
					throw new Exception(&quot;PNG format not supported (this shouldn&apos;t happen!).&quot;);
			}
			catch (Exception e) {
				JOptionPane.showMessageDialog(this, 
						&quot;Sorry, an error occurred while trying to save the image:
&quot; + e);
			}
		}
	}
/**
 * It was a slight pain needing to work with two files at once. I needed to
 * work with this file to edit it, but the TextCollage.java file to run the
 * program to test. Setting up this main() method to call the main() method of
 * the other file&apos;s class was just a shortcut so I only had to work with this
 * one file.
 * 
 * @param arguments Command line arguments
 */
	public static void main(String[] arguments) {
		TextCollage.main(arguments);
	}
}</code></pre>
</blockquote>
		<hr/>
		<p>
			Copyright © 2017 Alex Yst;
			You may modify and/or redistribute this document under the terms of the <a rel="license" href="/license/gpl-3.0-standalone.xhtml"><abbr title="GNU&apos;s Not Unix">GNU</abbr> <abbr title="General Public License version Three or later">GPLv3+</abbr></a>.
			If for some reason you would prefer to modify and/or distribute this document under other free copyleft terms, please ask me via email.
			My address is in the source comments near the top of this document.
			This license also applies to embedded content such as images.
			For more information on that, see <a href="/en/a/licensing.xhtml">licensing</a>.
		</p>
		<p>
			<abbr title="World Wide Web Consortium">W3C</abbr> standards are important.
			This document conforms to the <a href="https://validator.w3.org./nu/?doc=https%3A%2F%2Fy.st.%2Fen%2Fcoursework%2FCS1103%2FDrawTextPanel.java.xhtml"><abbr title="Extensible Hypertext Markup Language">XHTML</abbr> 5.1</a> specification and uses style sheets that conform to the <a href="http://jigsaw.w3.org./css-validator/validator?uri=https%3A%2F%2Fy.st.%2Fen%2Fcoursework%2FCS1103%2FDrawTextPanel.java.xhtml"><abbr title="Cascading Style Sheets">CSS</abbr>3</a> specification.
		</p>
	</body>
</html>

